// 2015年版权所有etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

// 包idutil实现用于生成唯一、
// 随机ID的实用函数。
package idutil

import (
	"math"
	"sync/atomic"
	"time"
)

const (
	tsLen     = 5 * 8
	cntLen    = 8
	suffixLen = tsLen + cntLen
)

// 生成器基于计数器、时间戳和
// 节点成员ID生成唯一标识符。
// 
// 初始ID的格式为：
// 高阶2个字节来自memberID，接下来的5个字节来自时间戳，
// 低阶1个字节是计数器。
// /|前缀|后缀| 
// /| 2字节| 5字节| 1字节| 
// /|成员ID |时间戳| cnt | 
// 
// 机器重启时，时间戳5字节不同于
// 1毫秒后和35年前。
// 
// 增加后缀以生成下一个id。
// 计数字段可能会溢出到时间戳字段，这是故意的。
// 它有助于将事件窗口扩展到2^56。这并没有破坏重启后生成的
// id是唯一的，因为etcd吞吐量<<
// 256req/ms（250k reqs/s）。
type Generator struct {
	// 高阶2字节
	prefix uint64
	// 低阶6字节
	suffix uint64
}

func NewGenerator(memberID uint16, now time.Time) *Generator {
	prefix := uint64(memberID) << suffixLen
	unixMilli := uint64(now.UnixNano()) / uint64(time.Millisecond/time.Nanosecond)
	suffix := lowbit(unixMilli, tsLen) << cntLen
	return &Generator{
		prefix: prefix,
		suffix: suffix,
	}
}

// 接下来生成一个唯一的id。
func (g *Generator) Next() uint64 {
	suffix := atomic.AddUint64(&g.suffix, 1)
	id := g.prefix | lowbit(suffix, suffixLen)
	return id
}

func lowbit(x uint64, n uint) uint64 {
	return x & (math.MaxUint64 >> (64 - n))
}
